
/**
 * @file Rx3ObsData.hpp
 * Encapsulate RINEX observation file data, including I/O
 *
 * Modified from gpstk::Rinex3ObsData, but remove complicated
 * inherited class FFTextStream:FFStream:fstream and FFData.
 * 
 * this new class is easy to read and use.
 *
 * copyright
 * Shoujian Zhang, 2020, Wuhan 
 */

#ifndef Rx3ObsData_HPP
#define Rx3ObsData_HPP

#include <vector>
#include <list>
#include <map>

#include "CommonTime.hpp"
#include "RinexDatum.hpp"
#include "DataStructures.hpp"
#include "Rx3ObsHeader.hpp"


namespace gnssbox
{

      /// @ingroup FileHandling
      //@{

      /// This class models a RINEX Observation Data Record.
      /// @sa gnssbox::Rx3ObsHeader.
      /// @sa rinex_obs_test.cpp for examples.
   class Rx3ObsData 
   {
   public:

         /// Constructor.
      Rx3ObsData();

         /// Destructor
      virtual ~Rx3ObsData() {}

      bool headerRead;
      bool headerWrite;

      Rx3ObsHeader header;


      Rx3ObsHeader auxHeader; 
      ///< auxiliary header records (epochFlag 2-5)

      /// Map from SatID to RinexDatum; order of the data matches the
      /// order of RinexObsIDs in the header
      typedef std::map<SatID, std::vector<RinexDatum> > DataMap;

         /// Time corresponding to the observations
      CommonTime time;

         /// Epoch flag has the following values
         /// 0 ok
         /// 1 power failure since previous epoch
         /// 2 start moving antenna
         /// 3 new site occupation (end moving antenna)
         ///   at least MARKER NAME header record follows
         /// 4 header records follow
         /// 5 external event
         /// 6 cycle slip record - same format as observation, but slips not data,
         ///   and LLI and SSI are blank
      short epochFlag;

         /// Number of satellites in this observation, except when
         /// epochFlag=2-5, then number of auxiliary header records to follow.
      short numSVs;

      double clockOffset;        ///< optional clock offset in seconds

      DataMap obs;               ///< the map of observations

      /// return gnssRinex
      gnssRinex gObs;


         /** This method returns the RinexDatum of a given observation
          *
          * @param sat     Satellite whose observation we want to fetch.
          * @param index   Index representing the observation type. It is
          *                obtained from corresponding RINEX Observation Header
          *                using method 'Rx3ObsHeader::getObsIndex()'.
          */
      virtual RinexDatum getObs( const SatID& svID, size_t index ) const
         noexcept(false);


         /** This method returns the RinexDatum of a given observation
          *
          * @param sat  Satellite whose observation we want to fetch.
          * @param type String representing the observation type.
          * @param hdr  Rx3ObsHeader for current RINEX file.
          */
      virtual RinexDatum getObs( const SatID& svID,
                                 const std::string& obsID,
                                 const Rx3ObsHeader& hdr ) const
         noexcept(false);

         /** This method returns the RinexDatum of a given observation
          *
          * @param svID  SatID of satellite
          * @param obsID RinexObsID  of the observation type.
          * @param hdr   Rx3ObsHeader for current RINEX file.
          */
      virtual RinexDatum getObs( const SatID& svID,
                                 const RinexObsID& obsID,
                                 const Rx3ObsHeader& hdr ) const
         noexcept(false);

         /** This sets the RinexDatum for a given observation
          *
          * @param data  RinexDatum of obs
          * @param svID  SatID of satellite
          * @param obsID RinexObsID  of the observation type.
          * @param hdr   Rx3ObsHeader for current RINEX file.
          */
      virtual void setObs( const RinexDatum& data,
                           const SatID& svID,
                           const RinexObsID& obsID,
                           const Rx3ObsHeader& hdr )
         noexcept(false);

         /// A Debug output function.
         /// Dumps the time of observations and the IDs of the Sats
         /// in the map.
      virtual void dump(std::ostream& s) const;

         /// Another dump, using information from the header
      void dump(std::ostream& s, Rx3ObsHeader& head) const;


      std::string timeString() const
         noexcept(false);

         /// Writes a correctly formatted record from this data to stream \a s.
         /// When printing comment records, you'll need to format them
         /// correctly yourself. This means making sure that "COMMENT" is at
         /// the end of the line and that they're the correct length
         /// (<= 80 chrs).
         /// Also, make sure to correctly set the epochFlag to the correct
         /// number for the type of header data you want to write.
      virtual void reallyPutRecord(std::fstream& strm) 
         noexcept(false);

      virtual void reallyPutRecordVer2(std::fstream& strm) 
         noexcept(false);


         /** This functions obtains a RINEX 3 Observation record from the given
          *  std::fstream. If there is an error in reading from the stream, it is
          *  reset to its original position and its fail-bit is set.
          *
          *  Because of the Rinex 3 Obs format, a Rx3ObsData record returned
          *  might not have data.  Check the Rinex3SatMap for empty() before
          *  using any data in it.
          *
          *  @throws StringException When a StringUtils function fails
          *  @throws std::fstreamError   When exceptions(failbit) is set and a read
          *          or formatting error occurs.  Also resets the stream to its
          *          pre-read position.
          */
      virtual void reallyGetRecord(std::fstream& strm)
         noexcept(false);

      virtual void reallyGetRecordVer2(std::fstream& strm)
         noexcept(false);


   private:


         /// Writes the CommonTime into RINEX 3 format.
         /// If it's a bad time, it will return blanks.
      std::string writeTime(const CommonTime& dt) const
         noexcept(false);


         /** This function constructs a CommonTime object from the given
          *  parameters.
          *
          * @param line The encoded time string found in the RINEX record.
          * @param hdr  The RINEX Observation Header object for the current
          *             RINEX file.
          */
      CommonTime parseTime( const std::string& line,
                            const Rx3ObsHeader& hdr,
                            const TimeSystem& ts) const
         noexcept(false);


   }; // End of class 'Rx3ObsData'

   // global re-define the operator >> for reading from file stream
   inline std::fstream& operator>>(std::fstream& strm, Rx3ObsData& data)
   {
       try
       {
            data.reallyGetRecord(strm);
            return strm;
       }
       catch(EndOfFile& e)
       {
           GPSTK_RETHROW(e);
       }
   }

   // global re-define the operator << for reading from file stream
   inline std::fstream& operator<<(std::fstream& strm, Rx3ObsData& data)
   {
       try
       {
          data.reallyPutRecord(strm);
          return strm;
       }
       catch(EndOfFile& e)
       {
           GPSTK_RETHROW(e);
       }
   }


      //@}

} // End of namespace gnssbox

#endif   // GPSTK_Rx3ObsData_HPP
